
Q.Node.prototype.type = "Node";
Q.Text.prototype.type = "Text";
Q.Group.prototype.type = "Group";
Q.Edge.prototype.type = "Edge";

var graph;

$(function(){
    Q.addCSSRule(".maximize", "margin: 0px !important;position: fixed !important;top: 0px;bottom: 0px;right: 0px;left: 0px;z-index: 1030;height: auto !important; width: auto !important;");
    graph = new Q.Graph("canvas");

    var styles = {};
    styles[Q.Styles.SELECTION_COLOR] = "#0F0";
    graph.styles = styles;

    graph.originAtCenter = false;
    graph.editable = true;

    graph.onElementCreated = function (element, evt) {
        if (element instanceof Q.Edge) {
            //var v = prompt("连线粗度");
            //默认线的粗度为1
            v = parseInt(1);
            if(v){
                element.setStyle(Q.Styles.EDGE_WIDTH, v);
            }else{
                graph.removeElement(element);
            }
            if(element.edgeType && element.edgeType != Q.Consts.EDGE_TYPE_DEFAULT){
                element.setStyle(Q.Styles.EDGE_BUNDLE, false);
            }
            return;
        }
        if (element instanceof Q.Text) {
            element.setStyle(Styles.LABEL_BACKGROUND_COLOR, "#2898E0");
            element.setStyle(Styles.LABEL_COLOR, "#FFF");
            element.setStyle(Styles.LABEL_PADDING, new Q.Insets(3, 5));
            return;
        }
    }

    graph.onLabelEdit = function(element, label, text, elementUI){
        if(!text){
            return false;
        }
        element.name = text;
        //此处调用后台保存
    }

    appendInteractionMenu(graph);

    initToolbar();
    initToolbox();
    requestDatas();

//监听面板调整大小事件，同步graph大小
    $('#center_panel').panel({
        onResize: function (w, h) {
            graph.updateViewport();
        }
    });

});

function ensureVisible(node){
    var bounds = graph.getUIBounds(node);
    var viewportBounds = graph.viewportBounds;
    if(!viewportBounds.contains(bounds)){
        graph.sendToTop(node);
        graph.centerTo(node.x, node.y);
    }
}

function setInteractionMode(evt, info){
    graph.interactionMode = info.value;
    currentInteractionMode = info.value;
    if(info.value == Q.Consts.INTERACTION_MODE_CREATE_EDGE){
        graph.edgeUIClass = info.edgeUIClass;
        graph.edgeType = info.edgeType;
    }
}
function initToolbar(){
    var toolbar = document.getElementById("toolbar");
    var buttons = {
        interactionModes:[{name: "默认模式", value:Q.Consts.INTERACTION_MODE_DEFAULT, selected: true, icon:'images/default_icon.png', action: setInteractionMode},
            {name: '框选模式', value:Q.Consts.INTERACTION_MODE_SELECTION, icon:'images/rectangle_selection_icon.png', action: setInteractionMode},
            {name: '浏览模式', value:Q.Consts.INTERACTION_MODE_VIEW, icon:'images/pan_icon.png', action: setInteractionMode}],
        zoom: [{name: "放大", icon:"images/zoomin_icon.png", action: function(){graph.zoomIn()}},
            {name: "缩小", icon:"images/zoomout_icon.png", action: function(){graph.zoomOut()}},
            {name: "1:1", action: function(){graph.scale = 1;}},
            {name: '纵览', icon:'images/overview_icon.png', action: function(){this.zoomToOverview()}},
            {name: '最大化', icon:'images/fullscreen_icon.png', action: function(){
                if($("#graph_panel").hasClass("maximize")){
                    $("#graph_panel").removeClass("maximize")
                }else{
                    $("#graph_panel").addClass("maximize")
                }
                graph.updateViewport();
            }}],
        find: {name: '查找', type: "input", action: function(evt, info){
            var name = info.input.value;
            if(!name){
                return;
            }
            graph.forEach(function(e){
                if(e instanceof Q.Node && (name == e.name || (e.info && e.info.name == name))){
                    graph.setSelection(e);
                    ensureVisible(e);
                    return false;
                }
            }, graph);
        }}
    };
    createToolBar(buttons, toolbar, graph, false, false);

}

function initToolbox(){
    var toolbox = document.getElementById("toolbox");

    createToolBar({a: [
        {name: '普通连线', value:Q.Consts.INTERACTION_MODE_CREATE_EDGE, icon:'images/edge_icon.png', action: setInteractionMode}]}, toolbox, graph,  "btn-group-vertical", false);

    createDNDImage(toolbox, "images/node_icon.png", "终端", {type: "Node", label: "Mac", image: "Q.Graphs.node"});
    createDNDImage(toolbox, "images/exchanger_icon.png", "交换机", {type: "Node", label: "交换机", image: "Q.Graphs.exchanger2"});
    createDNDImage(toolbox, "images/server_icon.png", "服务器", {type: "Node", label: "服务器", image: "Q.Graphs.server"});
}

var responseJson;
var path = 'http://localhost:8080/bigScreen/';

//初始化获取数据
function requestDatas() {
  //console.log('获取数据');
  $.ajax({
    url: path+'tmOrganization1',
    type:'POST',
    dataType:'json',
    success: function (data) {
      //处理json数据，按camp显示处理
      console.log(JSON.stringify(data));
      responseJson = data;
      var camp = data.ocgCamp;
      var index = camp.length;
      var relationsCollections = data.collections;
      while (index > 0){
        var campJson = data[camp[index-1]];
        var campName = camp[index-1];
        initDatas(campJson,relationsCollections,campName);
        index--;
      }
    },
    error: function(data){
      console.log('ajax请求失败');
    }

  });
}


//新增后数据保存
function addDatas(requestADDParam) {
    if (requestADDParam != undefined){
      $.ajax({
        url:path+'bigScreenAddDatas',
        data:requestADDParam,
        contentType : "application/json;charset=utf-8",
        type:'post',
        dataType: 'json',
        success:function (data) {

        },
        error:function (data) {
          alert('新增失败');
        }
      });
    }
    requestADDParam = undefined;
}

//删除后数据保存
function deleteDatas(requestDELParams) {
  $.ajax({
    url:path+'bigScreenDeleteDatas',
    data:requestDELParams,
    contentType : "application/json;charset=utf-8",
    type:'post',
    dataType:'json',
    success:function (data) {

    },
    error:function (data) {
        alert('删除失败')
    }
  });
}




function initDatas(campJson,relationsCollections,campName){
    Q.loadJSON(campJson, function(json){
        var topoNodes = campJson.nodes;
        var relations = relationsCollections;
        console.log(campName);
        initTopology(topoNodes,relations,campName);

        //todo
        graph.callLater(function(){
            var layouter = new Q.TreeLayouter(graph);
            layouter.layoutType = Q.Consts.LAYOUT_TYPE_EVEN_VERTICAL;
            layouter.doLayout();
            graph.moveToCenter();
        })

        var datas = [];
        var map = {};
        graph.graphModel.forEachByBreadthFirst(function(d){
          var name = d.name || d.type;
            var data = {text: name, id: d.id, iconCls: getTreeIcon(d)};
            map[d.id] = data;
            var parent = d.parent;
            if(!parent){
                datas.push(data);
                return;
            }
            parent = map[parent.id];
            var children = parent.children;
            if(!children){
                children = parent.children = [];
            }
            children.push(data);
        });

        $('#tree').tree({
            data: datas
        });
        syncSelectionTreeAndGraph("tree", graph);
        syncDataTreeAndGraph("tree", graph);
    });
}

function getTreeIcon(d){
    return d instanceof Q.Edge ? "edge_icon" : "node_icon";
}

//
var requestADDParam = undefined;
var requestDELParams = [];

var addParams = [];


//listChangeDispatcher 为递归循环
var rush = true;
//监听 ADD DELETE
function syncDataTreeAndGraph(treeId, graph){
    treeId = "#" + treeId;
    graph.listChangeDispatcher.addListener(function(evt){
        var data = evt.data;
        switch (evt.kind) {
            case Q.ListEvent.KIND_ADD :
              var map = {};
              var param = {};
              var key = undefined;
              if (data.$image == 'Q-node'&&(data.$name =='Mac' || data.$name == '交换机' || data.$name == '服务器')){
                key = 'node';
                if (data.$name =='Mac'){
                  param = {id:data.id,orgType:1}
                }
                if (data.$name == '交换机'){
                  param = {id:data.id,orgType:2}
                }
                if (data.$name == '服务器'){
                  param = {id:data.id,orgType:3}
                }
                map[key] = param;
                requestADDParam = map;
              }
              if ((data.name == 'Edge')&&(edgeIdCollections.indexOf(data.id) ==  -1)){
                  edgeIdCollections.push(data.id)
                  key = 'EDGE';
                  var edgType = data.edgeType != "orthogonal.H"? 1:2;
                  param = {fromId:data.from.id,toId:data.to.id,edgType:edgType};
                  map[key] = param;
                  requestADDParam = map;
              }
              if(addParams.indexOf(data.id) == -1){
                addDatas(JSON.stringify(requestADDParam));
              }

              addParams.push(data.id);

              var treeData = {data:[{id: data.id, text: data.name, iconCls: getTreeIcon(data)}]};
              $(treeId).tree('append', treeData);
              break;


            case Q.ListEvent.KIND_REMOVE :
                Q.forEach(data, function(node){
                    var requestArgs = node;
                    var node = $(treeId).tree('find', node.id);
                    var map = {};
                    if(node){
                        var popChildren = $(node.target)[0].children;
                        var lastIndex = popChildren.length-1;
                        var pop = $(node.target)[0].children[lastIndex].innerText;
                        var param = {};
                      if (pop == 'Edge')
                      {
                        var fromId = requestArgs.from.id;
                        var toId = requestArgs.to.id;
                        param = {fromId:fromId,toId:toId};
                        var key = 'EDGE';
                        map[key] = param;
                      }else{
                        var param = requestArgs.id;
                        var key = 'Node';
                        map[key] = param;
                      }
                      requestDELParams.push(map);
                      intervalAjax();
                      $(treeId).tree('remove', node.target);
                    }
                });
                break;
            case Q.ListEvent.KIND_CLEAR :
                break;
        }
    });
}


function intervalAjax() {
      var timer = setInterval(function () {
        if (requestDELParams.length != 0){
          console.log(JSON.stringify(requestDELParams));
          deleteDatas(JSON.stringify(requestDELParams));
        }
      requestDELParams.length = 0;
      },10);

}




//监听修改，选中
function syncSelectionTreeAndGraph(treeId, graph){
  //console.log(treeId);
    treeId = "#" + treeId;
    var selectionAjdusting;
    graph.selectionChangeDispatcher.addListener(function(evt){
        if(selectionAjdusting){
            return;
        }
        selectionAjdusting = true;
        var selection = [];
        graph.selectionModel.forEach(function(node){
            var node = $(treeId).tree('find', node.id);
            if(node){
                selection.push(node.target);
            }
            //选中点监听
          //console.log(node);
        });
        $(treeId).tree('select', selection);
        selectionAjdusting = false;
    });
    $(treeId).tree({onSelect: function(){
        if(selectionAjdusting){
            return;
        }
        selectionAjdusting = true;
        var selected = $(treeId).tree("getSelected");
        if(selected){
            var node = graph.getElement(selected.id);
            graph.selectionModel.set(node);
            if(node){
                ensureVisible(node);
            }
        }
        selectionAjdusting = false;
    }});
}

var edgeIdCollections = [];
function initTopology(topoNodes,topoRelations,campName)
{
    var nodeImage = campName == 'CN'?'Q-node':'image/node_icon.png';
    var exchangeImage = campName == 'CN'?'Q-exchanger2':'image/exchanger_icon/png';
    var serverImage = campName == 'CN'?'Q-server':'image/server_icon.png';
    var locationX = campName == 'CN'?500:-500;
    var locationY = campName == 'CN'?500:-500;
    var map = {};
    for(var i=0;i<topoNodes.length;i++)
    {
        var node = topoNodes[i];
        var qNode = new Q.Node();
        qNode.name=node.orgName+'['+node.orgCamp+']';

        if (node.orgType == 1){
          qNode.image= nodeImage;
        }
        if(node.orgType == 2){
          qNode.image = exchangeImage;
        }
        if(node.orgType == 3){
          qNode.image=serverImage;
        }
        graph.graphModel.add(qNode);

        // if (campName == 'CN'){
        //   qNode.rotate = Math.PI / 3;
        //   qNode.location = new Q.Point(locationX,locationY);
        // }

        qNode.location = new Q.Point(locationX,locationY);

        // if (node.orgMierarchy == 0 || node.orgMierarchy == 1 ){
        //   qNode.parentChildrenDirection = Q.Consts.DIRECTION_BOTTOM;
        //   qNode.layoutType = Q.Consts.LAYOUT_TYPE_EVEN_VERTICAL;
        // }else {
        //   qNode.parentChildrenDirection = Q.Consts.DIRECTION_BOTTOM;
        //   qNode.layoutType = Q.Consts.LAYOUT_TYPE_EVEN_VERTICAL;
        // }

      qNode.parentChildrenDirection = Q.Consts.DIRECTION_BOTTOM;
      qNode.layoutType = Q.Consts.LAYOUT_TYPE_EVEN;

        // console.log(qNode.location);


        map[node.id] = qNode;

    }


    for(var i=0;i<topoNodes.length;i++)
    {
        var node = topoNodes[i];
        var parent = node.parentId;
        if(parent){
            parent = map[parent];
            if(parent){
                map[node.id].parent = parent;
            }
        }
    }

    for(var i=0;i<topoRelations.length;i++)
    {
        var relation = topoRelations[i];
        var nodeFrom = map[relation.fromId];
        var nodeTo = map[relation.toId];
        if(nodeFrom && nodeTo){
            var edge = graph.createEdge(nodeFrom, nodeTo);
            edgeIdCollections.push(edge.id);
            if(relation.edgType == 2){
              edge.edgeType = Q.Consts.EDGE_TYPE_VERTICAL_HORIZONTAL;
            }
        }
    }

}

/**
 * 向下展开
 * @param error
 */
  function  unFoldDirection() {

  }


//dialog
function showError(error) {
  if(Q.isString(error)){
    showDialog("Error", error);
    return;
  }
  if(error.stack){
    showDialog(error.message, error.stack);
  }
}

function showDialog(title, content){
  if(content === undefined){
    content = title;
    title = null;
  }
  $("#q-message .modal-title").html(title || "消息");
  $("#q-message .modal-body").html(content);
  $('#q-message').modal("show");
}

function hideDialog(){
  $('#q-message').modal("hide");
}

//PopupMenu
function showDivAt(div, x, y){
  var body = document.documentElement;
  var bounds = new Q.Rect(window.pageXOffset, window.pageYOffset, body.clientWidth - 2, body.clientHeight - 2);
  var width = div.offsetWidth;
  var height = div.offsetHeight;

  if (x + width > bounds.x + bounds.width) {
    x = bounds.x + bounds.width - width;
  }
  if (y + height > bounds.y + bounds.height) {
    y = bounds.y + bounds.height - height;
  }
  if (x < bounds.x) {
    x = bounds.x;
  }
  if (y < bounds.y) {
    y = bounds.y;
  }
  div.style.left = x + 'px';
  div.style.top = y + 'px';
}
function isDescendant(parent, child) {
  var node = child.parentNode;
  while (node != null) {
    if (node == parent) {
      return true;
    }
    node = node.parentNode;
  }
  return false;
}
var PopupMenu = function(items){
  this.items = items || [];
}
var menuClassName = 'dropdown-menu';
PopupMenu.Separator = 'divider';

PopupMenu.prototype = {
  dom : null,
  _invalidateFlag: true,
  add : function(item){
    this.items.push(item);
    this._invalidateFlag = true;
  },
  addSeparator : function() {
    this.add(PopupMenu.Separator);
  },
  showAt: function(x, y){
    if(!this.items || !this.items.length){
      return false;
    }
    if(this._invalidateFlag){
      this.render();
    }
    this.dom.style.display = "block";
    document.body.appendChild(this.dom);
    showDivAt(this.dom, x, y);
  },
  hide : function(){
    if(this.dom && this.dom.parentNode){
      this.dom.parentNode.removeChild(this.dom);
    }
  },

  render : function(){
    this._invalidateFlag = false;
    if(!this.dom){
      this.dom = document.createElement('ul');
      this.dom.setAttribute("role", "menu");
      this.dom.className = menuClassName;
      var startEventName = Q.isTouchSupport ? "touchstart" : "mousedown";

      if(!this.stopEditWhenClickOnWindow){
        var _this = this;
        this.stopEditWhenClickOnWindow = function(evt){
          if(isDescendant(_this.html, evt.target)){
            _this.hide();
          }
        }
      }
      window.addEventListener("mousedown", this.stopEditWhenClickOnWindow, true);
      this.dom.addEventListener(startEventName, function(evt){
        Q.stopEvent(evt);
      }, false);
    }else{
      this.dom.innerHTML = "";
    }
    for (var i = 0,l = this.items.length; i < l; i++) {
      var item = this.renderItem(this.items[i]);
      this.dom.appendChild(item);
    }
  },
  html2Escape: function(sHtml) {
    return sHtml.replace(/[<>&"]/g,function(c){return {'<':'&lt;','>':'&gt;','&':'&amp;','"':'&quot;'}[c];});
  },
  renderItem : function(menuItem, zIndex){
    var dom = document.createElement('li');
    dom.setAttribute("role", "presentation");
    if(menuItem == PopupMenu.Separator){
      dom.className = PopupMenu.Separator;
      dom.innerHTML = " ";
      return dom;
    }
    if(Q.isString(menuItem)){
      dom.innerHTML = '<a role="menuitem" tabindex="-1" href="#">' + this.html2Escape(menuItem) + '</a>';
      return dom;
    }
    if(menuItem.selected){
      dom.style.backgroundPosition = '3px 5px';
      dom.style.backgroundRepeat = 'no-repeat';
      dom.style.backgroundImage = "url('')";
    }
    var a = document.createElement("a");
    a.setAttribute("role", "menuitem");
    a.setAttribute("tabindex", "-1");
    a.setAttribute("href", "javascript:void(0)");
    dom.appendChild(a);

    var text = menuItem.text || menuItem.name;
    if(text){
      a.innerHTML = this.html2Escape(text);
    }
    var className = menuItem.className;
    if(className){
      dom.className = className;
    }
    var call = menuItem.action;
    var self = this;

    var onclick = function(evt){
      if (call) {
        call.call(menuItem.scope, evt, menuItem);
      }
      if(!Q.isIOS){
        evt.target.focus();
      }
      setTimeout(function(){
        self.hide();
      }, 100);
    };
    if(Q.isTouchSupport){
//            dom.ontouchstart = onclick;
      a.ontouchstart = onclick;
    }else{
      dom.onclick = onclick;
    }
    return dom;
  }
}
Object.defineProperties(PopupMenu.prototype, {
  items: {
    get:function(){
      return this._items;
    },
    set: function(v){
      this._items = v;
      this._invalidateFlag = true;
    }
  }
});

function getPageXY(evt){
  if (evt.touches && evt.touches.length) {
    evt = evt.touches[0];
  }
  return {x: evt.pageX, y: evt.pageY};
}

function appendInteractionMenu(graph){
  var menu = new PopupMenu();
  function showMenu(evt, graph){
    var xy = getPageXY(evt);
    var x = xy.x, y = xy.y;
    var data = graph.getElementByMouseEvent(evt);
    var items = [];
    if (data) {
      var name = data instanceof Q.Node ? "Node" : "Edge";
      if (data.name) {
        name += " - " + data.name;
      }
      items.push({text: name, disabled: true});
      items.push(PopupMenu.Separator);
    }
    var currentMode = graph.interactionMode;
    var interactons = [
      {text: "平移模式", value: Q.Consts.INTERACTION_MODE_DEFAULT},
      {text: "框选模式", value: Q.Consts.INTERACTION_MODE_SELECTION}
    ];
    for (var i = 0, l = interactons.length; i < l; i++) {
      var mode = interactons[i];
      if (mode.value == currentMode) {
        mode.selected = true;
      }
      mode.action = function (evt, item) {
        graph.interactionMode = item.value;
      };
      items.push(mode)
    }
    items.push(PopupMenu.Separator);
    items.push({text: Q.name + ' - ' + Q.version});
    menu.items = items;
    menu.showAt(x, y);
    if (navigator.vibrate) {
      navigator.vibrate(200);
    }
  }
  if(Q.isTouchSupport){
    graph.addCustomInteraction({
      onlongpress: function (evt, graph) {
        showMenu(evt, graph)
      },
      onstart: function (evt) {
        menu.hide();
      }
    });
  }else{
    graph.html.oncontextmenu = function(evt){
      Q.stopEvent(evt);
      showMenu(evt, graph);
    }
    graph.addCustomInteraction({
      onstart: function (evt) {
        menu.hide();
      }
    });
  }
}

///ExportPanel
function ResizeBox(parent, onBoundsChange){
  this.onBoundsChange = onBoundsChange;
  this.parent = parent;
  this.handleSize = Q.isTouchSupport ? 20 : 8;

  this.boundsDiv = this._createDiv(this.parent);
  this.boundsDiv.type = "border";
  this.boundsDiv.style.position = "absolute";
  this.boundsDiv.style.border = "dashed 1px #888";
  var handles = "lt,t,rt,l,r,lb,b,rb";
  handles = handles.split(",");
  for(var i= 0,l=handles.length;i<l;i++){
    var name = handles[i];
    var handle = this._createDiv(this.parent);
    handle.type = "handle";
    handle.name = name;
    handle.style.position = "absolute";
    handle.style.backgroundColor = "#FFF";
    handle.style.border = "solid 1px #555";
    handle.style.width = handle.style.height = this.handleSize + "px";
    var cursor;
    if(name == 'lt' || name == 'rb'){
      cursor = "nwse-resize";
    }else if(name == 'rt' || name == 'lb'){
      cursor = "nesw-resize";
    }else if(name == 't' || name == 'b'){
      cursor = "ns-resize";
    }else{
      cursor = "ew-resize";
    }
    handle.style.cursor = cursor;
    this[handles[i]] = handle;
  }
  this.interaction = new Q.DragSupport(this.parent, this);
}
ResizeBox.prototype = {
  destroy: function(){
    this.interaction.destroy();
  },
  update: function(width, height){
    this.wholeBounds = new Q.Rect(0, 0, width, height);
    this._setBounds(this.wholeBounds.clone());
  },
  ondblclick: function(evt){
    if(this._bounds.equals(this.wholeBounds)){
      if(!this.oldBounds){
        this.oldBounds = this.wholeBounds.clone(). grow(-this.wholeBounds.height / 5, -this.wholeBounds.width / 5);
      }
      this._setBounds(this.oldBounds, true);
      return;
    }
    this._setBounds(this.wholeBounds.clone(), true);
  },
  startdrag: function(evt){
    if(evt.target.type){
      this.dragItem= evt.target;
    }
  },
  ondrag: function(evt){
    if(!this.dragItem){
      return;
    }
    Q.stopEvent(evt);
    var dx = evt.dx;
    var dy = evt.dy;
    if(this.dragItem.type == "border"){
      this._bounds.offset(dx, dy);
      this._setBounds(this._bounds, true);
    }else if(this.dragItem.type == "handle"){
      var name = this.dragItem.name;
      if(name[0] == 'l'){
        this._bounds.x += dx;
        this._bounds.width -= dx;
      }else if(name[0] == 'r'){
        this._bounds.width += dx;
      }
      if(name[name.length - 1] == 't'){
        this._bounds.y += dy;
        this._bounds.height -= dy;
      }else if(name[name.length - 1] == 'b'){
        this._bounds.height += dy;
      }
      this._setBounds(this._bounds, true);
    }

  },
  enddrag: function(evt){
    if(!this.dragItem){
      return;
    }
    this.dragItem = false;
    if(this._bounds.width < 0){
      this._bounds.x += this._bounds.width;
      this._bounds.width = -this._bounds.width;
    }else if(this._bounds.width == 0){
      this._bounds.width = 1;
    }
    if(this._bounds.height < 0){
      this._bounds.y += this._bounds.height;
      this._bounds.height = -this._bounds.height;
    }else if(this._bounds.height == 0){
      this._bounds.height = 1;
    }
    if(this._bounds.width > this.wholeBounds.width){
      this._bounds.width = this.wholeBounds.width;
    }
    if(this._bounds.height > this.wholeBounds.height){
      this._bounds.height = this.wholeBounds.height;
    }
    if(this._bounds.x < 0){
      this._bounds.x = 0;
    }
    if(this._bounds.y < 0){
      this._bounds.y = 0;
    }
    if(this._bounds.right > this.wholeBounds.width){
      this._bounds.x -= this._bounds.right - this.wholeBounds.width;
    }
    if(this._bounds.bottom > this.wholeBounds.height){
      this._bounds.y -= this._bounds.bottom - this.wholeBounds.height;
    }

    this._setBounds(this._bounds, true);
  },
  _createDiv: function(parent){
    var div = document.createElement("div");
    parent.appendChild(div);
    return div;
  },
  _setHandleLocation: function(handle, x, y){
    handle.style.left = (x - this.handleSize / 2) + "px";
    handle.style.top = (y - this.handleSize / 2) + "px";
  },
  _setBounds: function(bounds){
    if(!bounds.equals(this.wholeBounds)){
      this.oldBounds = bounds;
    }
    this._bounds = bounds;
    bounds = bounds.clone();
    bounds.width += 1;
    bounds.height += 1;
    this.boundsDiv.style.left = bounds.x + "px";
    this.boundsDiv.style.top = bounds.y + "px";
    this.boundsDiv.style.width = bounds.width + "px";
    this.boundsDiv.style.height = bounds.height + "px";

    this._setHandleLocation(this.lt, bounds.x, bounds.y);
    this._setHandleLocation(this.t, bounds.cx, bounds.y);
    this._setHandleLocation(this.rt, bounds.right, bounds.y);
    this._setHandleLocation(this.l, bounds.x, bounds.cy);
    this._setHandleLocation(this.r, bounds.right, bounds.cy);
    this._setHandleLocation(this.lb, bounds.x, bounds.bottom);
    this._setHandleLocation(this.b, bounds.cx, bounds.bottom);
    this._setHandleLocation(this.rb, bounds.right, bounds.bottom);
    if(this.onBoundsChange){
      this.onBoundsChange(this._bounds);
    }
  }
}
Object.defineProperties(ResizeBox.prototype, {
  bounds: {
    get: function(){
      return this._bounds;
    },
    set: function(v){
      this._setBounds(v);
    }
  }
});

function ExportPanel(){
  var scope = this;
  var export_panel = document.getElementById("export_panel");
  export_panel.addEventListener("mousedown", function(evt){
    if(evt.target == export_panel){
      scope.destroy();
    }
  }, false);
  var export_scale = document.getElementById("export_scale");
  var export_scale_label = document.getElementById("export_scale_label");
  export_scale.onchange = function(evt){
    export_scale_label.textContent = scope.scale = export_scale.value;
    scope.updateOutputSize();
  }
  var exportImage = function(print){
    var graph = scope.graph;
    if(!graph){
      return;
    }
    var scale = export_scale.value;
    var s = scope.imageInfo.scale;
    var clipBounds = new Q.Rect(scope.clipBounds.x / s, scope.clipBounds.y / s, scope.clipBounds.width / s, scope.clipBounds.height / s);
    clipBounds.offset(scope.bounds.x, scope.bounds.y);
    var imageInfo = graph.exportImage(scale, clipBounds);

    if (!imageInfo || !imageInfo.data) {
      return false;
    }
    var win = window.open();
    var doc = win.document;
    doc.title = graph.name || "";
//        doc.title = "export image - " + imageInfo.width + " x " + imageInfo.height;
    var img = doc.createElement("img");
    img.src = imageInfo.data;
    doc.body.style.textAlign = "center";
    doc.body.style.margin = "0px";
    doc.body.appendChild(img);

    if(print){
      var style = doc.createElement("style");
      style.setAttribute("type", "text/css");
      style.setAttribute("media", "print");
      var printCSS = "img {max-width: 100%; max-height: 100%;}";
      if(clipBounds.width / clipBounds.height > 1.2){
        printCSS += "\n @page { size: landscape; }";
      }
      style.appendChild(document.createTextNode(printCSS));
      doc.head.appendChild(style);

      img.style.maxWidth = "100%";
      img.style.maxHeight = "100%";

      setTimeout(function(){
        win.print();
        win.onfocus=function(){ win.close();}
      }, 100);
    }
  }
  var export_submit = document.getElementById("export_submit");
  export_submit.onclick = function(){
    exportImage();
  }
  var print_submit = document.getElementById("print_submit");
  print_submit.onclick = function(){
    exportImage(true);
  }
}
ExportPanel.prototype = {
  canvas: null,
  initCanvas: function(){
    var export_canvas = document.getElementById('export_canvas');
    export_canvas.innerHTML = "";

    var canvas = document.createElement("canvas");
    export_canvas.appendChild(canvas);
    this.canvas = canvas;

    var export_bounds = document.getElementById("export_bounds");
    var export_size = document.getElementById("export_size");
    var scope = this;
    var clipBounds;
    var drawPreview = function(){
      var canvas = scope.canvas;
      var g = canvas.getContext("2d");
      g.clearRect(0, 0, canvas.width, canvas.height);
      g.drawImage(scope.imageInfo.canvas, 0, 0);
      g.beginPath();
      g.moveTo(0, 0);
      g.lineTo(canvas.width, 0);
      g.lineTo(canvas.width, canvas.height);
      g.lineTo(0, canvas.height);
      g.lineTo(0, 0);

      var x = clipBounds.x, y = clipBounds.y, width = clipBounds.width, height = clipBounds.height;
      g.moveTo(x, y);
      g.lineTo(x, y + height);
      g.lineTo(x + width, y + height);
      g.lineTo(x + width, y);
      g.closePath();
      g.fillStyle = "rgba(0, 0, 0, 0.3)";
      g.fill();
    }
    var onBoundsChange = function(bounds){
      clipBounds = bounds;
      scope.clipBounds = clipBounds;
      drawPreview();
      var w = clipBounds.width / scope.imageInfo.scale | 0;
      var h = clipBounds.height / scope.imageInfo.scale | 0;
      export_bounds.textContent = (clipBounds.x / scope.imageInfo.scale | 0) + ", "
        + (clipBounds.y / scope.imageInfo.scale | 0) + ", " + w + ", " + h;
      scope.updateOutputSize();
    }
    this.updateOutputSize = function(){
      var export_scale = document.getElementById("export_scale");
      var scale = export_scale.value;
      var w = clipBounds.width / scope.imageInfo.scale * scale | 0;
      var h = clipBounds.height / scope.imageInfo.scale  * scale | 0;
      var info = w + " X " + h;
      if(w * h > 3000 * 4000){
        info += "<span style='color: #F66;'>图幅太大，导出时可能出现内存不足</span>";
      }
      export_size.innerHTML = info;
    }
    var resizeHandler = new ResizeBox(canvas.parentNode, onBoundsChange);
    this.update = function(){
      this.canvas.width = this.imageInfo.width;
      this.canvas.height = this.imageInfo.height;
      resizeHandler.update(this.canvas.width, this.canvas.height);
    }
  },
  destroy: function(){
    this.graph = null;
    this.imageInfo = null
    this.clipBounds = null;
    this.bounds = null;
  },
  show: function(graph){
    $('#export_panel').modal("show");

    this.graph = graph;
    var bounds = graph.bounds;
    this.bounds = bounds;

    var canvas_size = document.getElementById("canvas_size");
    canvas_size.textContent = (bounds.width | 0) + " X " + (bounds.height | 0);

    var size = Math.min(500, screen.width / 1.3);
    var imageScale;
    if(bounds.width > bounds.height){
      imageScale = Math.min(1, size / bounds.width);
    }else{
      imageScale = Math.min(1, size / bounds.height);
    }
    this.imageInfo = graph.exportImage(imageScale);
    this.imageInfo.scale = imageScale;

    if(!this.canvas){
      this.initCanvas();
    }
    this.update();
  }
}
var exportPanel;
function showExportPanel(graph){
  if(!exportPanel){
    exportPanel = new ExportPanel();
  }
  exportPanel.show(graph);
}

///drag and drop
var DRAGINFO_PREFIX = "draginfo";

function ondrag(evt) {
  evt = evt || window.event;
  var dataTransfer = evt.dataTransfer;
  var img = evt.target;
  dataTransfer.setData("text", img.getAttribute(DRAGINFO_PREFIX));
}


//创建点
function createDNDImage(parent, src, title, info) {
  var img = document.createElement("img");
  img.src = src;
  img.setAttribute("draggable", "true");
  img.setAttribute("title", title);
  info = info || {};
  if (!info.image && (!info.type || info.type == "Node")) {
    info.image = src;
  }
  info.label = info.label || title;
  info.title = title;
  img.setAttribute(DRAGINFO_PREFIX, JSON.stringify(info));
  img.ondragstart = ondrag;
  parent.appendChild(img);
  return img;
}

//创建线
function createToolBar(buttons, toolbar, scope, vertical, togglable){
  for(var n in buttons){
    var info = buttons[n];
    if(Q.isArray(info)){
      var buttonGroup = document.createElement("div");
      buttonGroup.className = vertical ? "btn-group-vertical" : "btn-group";
      if(togglable !== false){
        buttonGroup.setAttribute("data-toggle", "buttons");
      }
      for(var i= 0,l=info.length;i<l;i++){
        if(!info[i].type && togglable !== false){
          info[i].type = 'radio';
        }
        buttonGroup.appendChild(createGraphButton(info[i], scope));
      }
      toolbar.appendChild(buttonGroup);
      continue;
    }
    toolbar.appendChild(createGraphButton(info, scope));
  }
}

/**
 * 生成uuid
 * @param info
 * @param scope
 * @returns {HTMLButtonElement|HTMLLabelElement|HTMLDivElement}
 */
function guid() {
  return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
    var r = Math.random()*16|0, v = c == 'x' ? r : (r&0x3|0x8);
    return v.toString(16);
  });
}
function createGraphButton(info, scope){
  if(info.type == "input"){
    var div = document.createElement("div");
    div.style.display = "inline-block";
    div.style.verticalAlign = "middle";
    div.innerHTML = '<div class="input-group input-group-sm" style="width: 150px;">\
            <input type="text" class="form-control">\
                <span class="input-group-btn">\
                    <button class="btn btn-default" type="button"></button>\
                </span>\
            </div>';
    var input = div.getElementsByTagName("input")[0];
    var button = div.getElementsByTagName("button")[0];
    button.innerHTML = info.name;
    info.input = input;
    if(info.action){
      button.onclick = function(evt){
        info.action.call(scope || window.graph, evt, info);
      }
    }
    return div;
  }



  if(!info.type){
    var label = document.createElement("button");
  }else{
    var label = document.createElement("label");
    var button = document.createElement("input");
    info.input = button;
    button.setAttribute('type', info.type);
    label.appendChild(button);
    if (info.selected) {
      button.setAttribute('checked', 'checked');
      if(info.type == 'radio'){
        label.className += "active";
      }
    }
  }
  label.className += " btn btn-default btn-sm";
  if(info.icon){
    var icon = document.createElement('img');
    icon.src = info.icon;
    label.appendChild(icon);
  }else if(info.name){
    label.appendChild(document.createTextNode(" " + info.name));
  }
  if(info.name){
    label.setAttribute("title", info.name);
  }
  if(info.action){
    label.onclick = function(evt){
      info.action.call(scope || window.graph, evt, info);
    }
  }
  return label;
}
